/*
 * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.truffle.r.nodes.unary;

import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.r.runtime.RDeparse;
import com.oracle.truffle.r.runtime.data.RDataFactory;
import com.oracle.truffle.r.runtime.data.RLanguage;
import com.oracle.truffle.r.runtime.data.RNull;
import com.oracle.truffle.r.runtime.data.RStringVector;
import com.oracle.truffle.r.runtime.data.RSymbol;
import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
import com.oracle.truffle.r.runtime.data.model.RAbstractVector;

public abstract class CastStringNode extends CastStringBaseNode {

    private final boolean convertEmptyVectorToNull;

    protected CastStringNode(boolean preserveNames, boolean preserveDimensions, boolean preserveAttributes, boolean convertEmptyVectorToNull) {
        super(preserveNames, preserveDimensions, preserveAttributes);
        this.convertEmptyVectorToNull = convertEmptyVectorToNull;
    }

    public boolean convertEmptyVectorToNull() {
        return convertEmptyVectorToNull;
    }

    public abstract Object executeString(int o);

    public abstract Object executeString(double o);

    public abstract Object executeString(byte o);

    public abstract Object executeString(Object o);

    @Specialization
    protected RNull doNull(@SuppressWarnings("unused") RNull operand) {
        return RNull.instance;
    }

    @Specialization(guards = "vector.getLength() == 0")
    protected Object doEmptyVector(@SuppressWarnings("unused") RAbstractVector vector) {
        return convertEmptyVectorToNull ? RNull.instance : RDataFactory.createStringVector(0);
    }

    private RStringVector vectorCopy(RAbstractContainer operand, String[] data) {
        RStringVector ret = RDataFactory.createStringVector(data, operand.isComplete(), getPreservedDimensions(operand), getPreservedNames(operand));
        preserveDimensionNames(operand, ret);
        if (preserveAttributes()) {
            ret.copyRegAttributesFrom(operand);
        }
        return ret;
    }

    @Specialization(guards = "vector.getLength() != 0")
    protected RStringVector doStringVector(RStringVector vector) {
        if (preserveAttributes() && preserveDimensions() && preserveNames()) {
            return vector;
        } else {
            return vectorCopy(vector, vector.getDataCopy());
        }
    }

    @Specialization(guards = "operand.getLength() != 0")
    protected RStringVector doIntVector(RAbstractContainer operand) {
        String[] sdata = new String[operand.getLength()];
        // conversions to character will not introduce new NAs
        for (int i = 0; i < operand.getLength(); i++) {
            Object o = operand.getDataAtAsObject(i);
            if (o instanceof RLanguage) {
                sdata[i] = RDeparse.deparse(o);
            } else {
                sdata[i] = toString(o);
            }
        }
        return vectorCopy(operand, sdata);
    }

    @Specialization
    protected String doRSymbol(RSymbol s) {
        return s.getName();
    }

    public static CastStringNode create() {
        return CastStringNodeGen.create(true, true, true, false);
    }

    public static CastStringNode createNonPreserving() {
        return CastStringNodeGen.create(false, false, false, false);
    }
}
